﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Threading;
using NielsHebling.UnitTestResultViewerForSL.Controls;
using NielsHebling.UnitTestResultViewerForSL.Core.Data;
using NielsHebling.UnitTestResultViewerForSL.Core.Xml;

namespace NielsHebling.UnitTestResultViewerForSL.Core.Logic
{
    public class TestExecutionWorker : INotifyPropertyChanged
    {
        private Stopwatch _stopwatch;
        private List<string> _xapList;
        private string _testResultsOutputPath;
        private ConcurrentQueue<string> _resultXmlQueue;
        private static object lockObj = new object();
        private ObservableCollection<ViewResultData> _resultItems;
        public ObservableCollection<ViewResultData> ResultItems
        {
            get 
            {
                lock (lockObj)
                {
                    return _resultItems;
                }
            }
            set 
            {
                lock (lockObj)
                {
                    _resultItems = value;
                }
            }
        }

        private ResultList _control;

        private ViewResultDataList _resultData;
        public ViewResultDataList ResultData
        {
            get { return _resultData; }
            set { _resultData = value; }
        }        

        public TestExecutionWorker() 
        {
            _stopwatch = new Stopwatch();
        }

        public void InitializeTasks(string testResultsOutputPath, List<string> xapList, ResultList control)
        {
            _stopwatch.Start();
            _xapList = xapList;
            _control = control;
            _testResultsOutputPath = testResultsOutputPath;
            _control.Items = new ObservableCollection<ViewResultData>();
            _resultXmlQueue = new ConcurrentQueue<string>();            
            _resultItems = new ObservableCollection<ViewResultData>();
            _resultData = new ViewResultDataList();           
        }

        public void RunTasks()
        {
            VsShellAbstractionLayer.Instance.Trace("Start parallel foreach on XAP's = " + _stopwatch.Elapsed.Seconds + "sec " + _stopwatch.Elapsed.Milliseconds + "ms");
            SetMaxCount(_xapList.Count + 1);
            IncreaseCount();

            if (StatLightManager.Instance.StatLightVersion == StatLightVersion.v11)
            {
                //38sec 75ms
                Parallel.ForEach(_xapList, xap =>
                {
                    VsShellAbstractionLayer.Instance.Trace("Processing item:" + xap);
                    _resultXmlQueue.Enqueue(CreateTestResult(xap));
                    TryRunDeserialization();
                });
            }
            else if(StatLightManager.Instance.StatLightVersion == StatLightVersion.v12)
            {                
                Task.Factory.StartNew(() =>
                {
                    _resultXmlQueue.Enqueue(CreateMultipleTestResults(_testResultsOutputPath, _xapList));
                    TryRunDeserialization();
                });
            }
        }

        private string CreateTestResult(string xapFileName)
        {
            VsShellAbstractionLayer.Instance.Trace("Start test = " + _stopwatch.Elapsed.Seconds + "sec " + _stopwatch.Elapsed.Milliseconds + "ms");
            
            string result = StatLightManager.Instance.RunTest(xapFileName);

            if (!String.IsNullOrWhiteSpace(result))
            {
                VsShellAbstractionLayer.Instance.Trace("End test = " + result + " = " + _stopwatch.Elapsed.Seconds + "sec " + _stopwatch.Elapsed.Milliseconds + "ms");
                return result;
            }
            return string.Empty;
        }

        private string CreateMultipleTestResults(string testResultsOutputPath, List<string> xapFiles)
        {
            VsShellAbstractionLayer.Instance.Trace("Start test = " + _stopwatch.Elapsed.Seconds + "sec " + _stopwatch.Elapsed.Milliseconds + "ms");

            string result = StatLightManager.Instance.RunMultipleTests(testResultsOutputPath, xapFiles);

            if (!String.IsNullOrWhiteSpace(result))
            {
                VsShellAbstractionLayer.Instance.Trace("End test = " + result + " = " + _stopwatch.Elapsed.Seconds + "sec " + _stopwatch.Elapsed.Milliseconds + "ms");
                return result;
            }
            return string.Empty;
        }

        private void TryRunDeserialization()
        {
            //Is result queue empty
            if (!_resultXmlQueue.IsEmpty)
            {
                string queueItem;
                //Try get path to test result xml
                if (_resultXmlQueue.TryDequeue(out queueItem))
                {
                    if (!String.IsNullOrWhiteSpace(queueItem))
                    {
                        //Deserialize
                        DeserializeResult(queueItem);
                    }
                }
            }
        }

        private void DeserializeResult(string result)
        {
            VsShellAbstractionLayer.Instance.Trace("Start Deserialize = " + _stopwatch.Elapsed.Seconds + "sec " + _stopwatch.Elapsed.Milliseconds + "ms");
            Serializer serializer = new Serializer();
            FileInfo fi = new FileInfo(result);
            if (fi.Exists)
            {
                Result testResultItem = serializer.ReadXML<Result>(result);
                VsShellAbstractionLayer.Instance.Trace("End Deserialize = " + _stopwatch.Elapsed.Seconds + "sec " + _stopwatch.Elapsed.Milliseconds + "ms");
                if (testResultItem != null)
                {
                    AddResults(testResultItem);                    
                }
            }
        }

        private void AddResults(Result resultItem)
        {
            VsShellAbstractionLayer.Instance.Trace("Add Data = " + _stopwatch.Elapsed.Seconds + "sec " + _stopwatch.Elapsed.Milliseconds + "ms");
            this.ResultData.TestsFailed += resultItem.TestsFailed;
            this.ResultData.TestsIgnored += resultItem.TestsIgnored;
            this.ResultData.TestsTotal += resultItem.TestsTotal;
            NotifyPropertyChanged("ResultData");

            foreach (Tests tests in resultItem.TestResults)
            {
                foreach (Test testItem in tests.Items)
                {
                    ViewResultData data = new ViewResultData();
                    data.DateRun = resultItem.DateRun;
                    data.Duration = testItem.Duration;
                    data.Message = testItem.ExceptionInfo.Message;
                    data.StackTrace = testItem.ExceptionInfo.StackTrace;
                    data.Result = testItem.Result;
                    data.Method = testItem.Method;
                    data.TestsFailed = resultItem.TestsFailed;
                    data.TestsIgnored = resultItem.TestsIgnored;
                    data.TestsTotal = resultItem.TestsTotal;
                    data.XapFileName = resultItem.XapFileName;

                    //_resultItems.Add(data);
                    if (_control.Dispatcher.CheckAccess())
                    {
                        _control.Items.Add(data);
                    }
                    else
                    {
                        _control.Dispatcher.BeginInvoke(DispatcherPriority.Send, (Action)delegate { _control.Items.Add(data); });
                    }
                    NotifyPropertyChanged("ResultItems");
                }
            }
            
            IncreaseCount();
            VsShellAbstractionLayer.Instance.Trace("Results added = " + _stopwatch.Elapsed.Seconds + "sec " + _stopwatch.Elapsed.Milliseconds + "ms");
        }

        private void IncreaseCount()
        {
            if (_control.Dispatcher.CheckAccess())
            {
                _control.ProgressValue += 1;
            }
            else
            {
                _control.Dispatcher.BeginInvoke(DispatcherPriority.Send, (Action)delegate { _control.ProgressValue += 1; });
            }
        }

        private void SetMaxCount(int max)
        {
            if (_control.Dispatcher.CheckAccess())
            {
                _control.MaxProgressValue = max;
            }
            else
            {
                _control.Dispatcher.BeginInvoke(DispatcherPriority.Send, (Action)delegate { _control.MaxProgressValue = max; });
            }            
        }
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
    }
}
